package cn.cfoodc.sharding;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.shardingsphere.driver.api.ShardingSphereDataSourceFactory;
import org.apache.shardingsphere.infra.config.algorithm.ShardingSphereAlgorithmConfiguration;
import org.apache.shardingsphere.infra.config.algorithm.ShardingSphereAlgorithmPropertiesAware;
import org.apache.shardingsphere.sharding.algorithm.keygen.SnowflakeKeyGenerateAlgorithm;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.sharding.rule.ShardingRule;
import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;


@SpringBootApplication(scanBasePackages = {"cn.cfoodc.sharding"})
@MapperScan(value ="cn.cfoodc.sharding.mapper")
@Import(TransactionConfiguration.class)
public class ShardingApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShardingApplication.class, args);

    }

}

@Configuration
@EnableTransactionManagement
@Component
class TransactionConfiguration {

    @Bean
    public PlatformTransactionManager txManager(final DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }


    @Bean
    public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    //配置雪花算法实例
    @Bean
    public SnowflakeKeyGenerateAlgorithm snowflakeKeyGenerateAlgorithm(){
        SnowflakeKeyGenerateAlgorithm snowflakeKeyGenerateAlgorithm = new SnowflakeKeyGenerateAlgorithm();
        snowflakeKeyGenerateAlgorithm.init();//初始化偏移量，不然在业务执行消耗时间长 会累计产生偶数雪花ID
        return snowflakeKeyGenerateAlgorithm;
    }

    @Bean
    public DataSource createD() throws SQLException {

// 配置真实数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>();

// 配置第 1 个数据源
        HikariDataSource dataSource1 = new HikariDataSource();
        dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource1.setJdbcUrl("jdbc:mysql://47.113.207.101:3306/ds0?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false");
        dataSource1.setUsername("root");
        dataSource1.setPassword("123456");
        dataSource1.setIdleTimeout(600000);
        dataSource1.setMaximumPoolSize(20);
        dataSource1.setAutoCommit(true);
        dataSource1.setMaxLifetime(120000);
        dataSource1.setConnectionTimeout(30000);
        dataSource1.setConnectionTestQuery("SELECT 1");
        dataSourceMap.put("ds0", dataSource1);

// 配置第 2 个数据源
        HikariDataSource dataSource2 = new HikariDataSource();
        dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource2.setJdbcUrl("jdbc:mysql://47.113.207.101:3307/ds1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false");
        dataSource2.setUsername("root");
        dataSource2.setPassword("123456");
        dataSource2.setIdleTimeout(600000);
        dataSource2.setMaximumPoolSize(20);
        dataSource2.setAutoCommit(true);
        dataSource2.setMaxLifetime(120000);
        dataSource2.setConnectionTimeout(30000);
        dataSource2.setConnectionTestQuery("SELECT 1");
        dataSourceMap.put("ds1", dataSource2);

// 配置 t_order_item 表规则
        ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_order_item", "ds0.t_order_item${0..2}");

// 配置分库策略
        orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "dbShardingAlgorithm"));

// 配置分表策略
        orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_item_id", "tableShardingAlgorithm"));

// 配置 t_order_item 表规则...
// ...

// 配置分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTables().add(orderTableRuleConfig);

// 配置分库算法
        Properties dbShardingAlgorithmrProps = new Properties();
        dbShardingAlgorithmrProps.setProperty("algorithm-expression", "ds$0");
        shardingRuleConfig.getShardingAlgorithms().put("dbShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", dbShardingAlgorithmrProps));

// 配置分表算法
        Properties tableShardingAlgorithmrProps = new Properties();
        tableShardingAlgorithmrProps.setProperty("algorithm-expression", "t_order_item${order_item_id % 3}");
        shardingRuleConfig.getShardingAlgorithms().put("tableShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tableShardingAlgorithmrProps));

//分布式序列算法
        Properties result = new Properties();
        result.setProperty("worker-id", "123");
        //result.setProperty("max-vibration-offset","3");
        //result.setProperty("sequenceOffset","2");
        shardingRuleConfig.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration("SNOWFLAKE", result));
        orderTableRuleConfig.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("orderItemId", "snowflake"));

//sq日志输出
        Properties result2 = new Properties();
        result2.setProperty("sql-show","true");

// 创建 ShardingSphereDataSource
        DataSource dataSource = ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig),  result2);

        return dataSource;
    }

}

